#ifndef _CRBET_H_
#define _CRBET_H_

class CrapsBet
{
	public:

		// constructors
		CrapsBet();
		CrapsBet(uint8 betID);
		CrapsBet(uint8 betID, int32 amount, int32 bonusAmount);
		CrapsBet(const CrapsBet &bet);

		// destructor
		~CrapsBet(void) {};

		// virtuals
		virtual CrapsBet&	operator=(const CrapsBet &bet);
		virtual bool8		canRemove(uint8 point) { return true; }		// can remove most bets
		virtual bool8		payoutPersistant() { return false; }		// most bets are not left after a payout
		virtual CrapsBet *	getDepends() { return (CrapsBet *)0; }		// most bets do not depend on others
		virtual int32		getLimit(uint8 point, int32 maxLimit);
		virtual void		rollDone (uint8 point) {};
		// Can we
		virtual bool8		getRemoveDependentBet (void) {return true;};

		// accessors
		int32		getAmount(void);
		int32		getBonusAmount(void);
		uint8		getBetID() { return m_betID; }
		const String& getName(uint8 betID);
		CrapsBet *	getDependent(void);

		// modifiers
		void		setID(uint8 betID);
		virtual void setAmount(int32 amount);
		void		setBonusAmount(int32 bonusAmount);
		void		setDependant(CrapsBet *bet);

		void		grandfathered(bool8 onoff);
		bool8		isGrandfathered() { return m_grandfathered; }

		enum BetState
		{
			LOST				=  0,
			WON					=  1,
			CARRY				= -1,
			PUSH				= -2,
			MOVECOME			= -3,		// MOVECOME - (4,5,6,8,9,10) denotes Come should be moved to Come Number
			MOVEDONTCOME		= -4 - 10	// MOVEDONTCOME follows same idea
		};


		enum BetTypes
		{
			Pass, DontPass,
			PassOdds, DontPassOdds,
			Field, Big6, Big8,
			Come, DontCome,
			Lay4, Come4, DontCome4, Come4Odds, DontCome4Odds, Buy4, Place4, Place4Lose,
			Lay5, Come5, DontCome5, Come5Odds, DontCome5Odds, Buy5, Place5, Place5Lose,
			Lay6, Come6, DontCome6, Come6Odds, DontCome6Odds, Buy6, Place6, Place6Lose,
			Lay8, Come8, DontCome8, Come8Odds, DontCome8Odds, Buy8, Place8, Place8Lose,
			Lay9, Come9, DontCome9, Come9Odds, DontCome9Odds, Buy9, Place9, Place9Lose,
			Lay10, Come10, DontCome10, Come10Odds, DontCome10Odds, Buy10, Place10, Place10Lose,
			CircleAnyCraps,
			YoEleven,
			Seven,
			AnyCraps,
			Hard4, Hard6, Hard10, Hard8,
			Craps3,
			SnakeEyes,
			Eleven,
			BoxCars,
			/*
			Horn,
			*/

			NUMBETS
		};

	protected:

		uint8		 m_betID;
		int32		 m_amount;
		int32		 m_bonusAmount; // Amount of bet that came from the bonus balance
		CrapsBet	*m_dependant;
		bool8		 m_grandfathered;
};


class DependsBet : public CrapsBet
{
	public:
		CrapsBet *	getDepends(void) { return m_depends; }

	protected:
		DependsBet(uint8 betID, CrapsBet* depends) : CrapsBet(betID) { m_depends = depends; depends->setDependant(this); }
		DependsBet(uint8 betID, const DependsBet &bet);

		DependsBet& operator=(const DependsBet &bet);

	protected:
		CrapsBet *m_depends;
};


class OddsBet : public DependsBet
{
	public:
		OddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			DependsBet(betID, depends) { m_oddsMult = oddsMult; }

		OddsBet(uint8 betID, const OddsBet &bet);

		OddsBet& operator=(const OddsBet &bet);


		virtual int32 getLimit(uint8 point, int32 maxLimit) = 0;


	protected:
		int32 	*m_oddsMult;
};



class PassBet : public CrapsBet
{
	public:
		PassBet(uint8 betID) : CrapsBet(betID) { }
		PassBet(uint8 betID, const PassBet &bet);

		bool8 canRemove(uint8 point)
		{
			// pass line bets only removed before a point is established
			return (bool8)(point == 0);
		}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			if (point > 0)	// can't bet on this after point is made
				return 0;
			else
				return maxLimit;
		}
};

class DontPassBet : public CrapsBet
{
	protected:
		int32	m_oldMaxAmount;
		uint8	m_point;

	public:

		DontPassBet(uint8 betID) : CrapsBet(betID) {m_oldMaxAmount = 0; m_point = 0;}
		DontPassBet(uint8 betID, const DontPassBet &bet);

		virtual void rollDone ()
		{
			m_oldMaxAmount = m_amount;
		}
		virtual void setAmount(int32 amount)
		{
			if (m_point == 0)
				m_oldMaxAmount = amount;
			m_amount = amount;
		}

		virtual bool8 getRemoveDependentBet (void) {return false;};
		
		//RPF 20090813 CSN-511
		// CGUERRA: discomment function. DontPass bet is not allowed to be removed
		bool8 canRemove(uint8 point)
		{
			// dont pass bets only removed before a point is established
			return (bool8)(point == 0);
		}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			m_point = point;
			if (point > 0)	// can't bet on this after point is made
				return m_oldMaxAmount?m_oldMaxAmount:m_amount;
			else
				return maxLimit;
		}

};


class PassOddsBet : public OddsBet
{
	public:
		PassOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			OddsBet(betID, depends, oddsMult) {}

		PassOddsBet(uint8 betID, const PassOddsBet &bet) :
			OddsBet(betID, bet) {}


		int32 getLimit(uint8 point, int32 maxLimit)
		{
			int32 limit = 0;

			if (m_depends && m_oddsMult)
			{
				switch (point)
				{

					case 0:
						limit = 0;
						break;

					default:
						limit = (int32) (*m_oddsMult * m_depends->getAmount() );
						if (limit > maxLimit)
							limit = maxLimit;
						break;
				}
			}
			return limit;
		}

#if 0
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			switch (point)
			{

				case 0:
					return 0;

				// Full double (2.5 x):
				// for bets >= $4 but not even, round down to the nearest multiple of $5 (~ 500 cents)
				//     bets <  $4 give 2x odds
				case 6:
				case 8:
					if (m_depends)
					{
						int32 limit = 0;

						if (*m_oddsMult == 25)
						{
							if (m_depends->getAmount() < 400)
								limit = (int32) (2 * m_depends->getAmount() );
							else if ( (m_depends->getAmount() / 100) % 2 != 0)
								limit = (int32)( ((*m_oddsMult * m_depends->getAmount() / 10) / 500) * 500 );
							else
								limit = (int32) (*m_oddsMult * m_depends->getAmount() / 10);
						}
						else
						{
							limit = (int32) (*m_oddsMult / 10 * m_depends->getAmount() );
						}

						if (limit > maxLimit)
							return maxLimit;
						else
							return limit;

					}
					else
						return 0;

				default:
					if (m_depends)
					{
						int32 limit	= 0;
						uint8 mult	= (uint8)(*m_oddsMult);

						if (mult == 25)
							mult = 2;
						else
							mult = mult / 10;

						limit = (int32)(mult * m_depends->getAmount() );

						if (limit > maxLimit)
							return maxLimit;
						else
							return limit;
					}
					else
						return 0;
			}
		}
#endif
};


class DontPassOddsBet : public OddsBet
{
	public:
		DontPassOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			OddsBet(betID, depends, oddsMult) {}

		DontPassOddsBet(uint8 betID, const DontPassOddsBet &bet) :
			OddsBet(betID, bet) {}

//		virtual	void setAmount(int32 amount)
//		{
//			int32 limit = m_depends->getAmount() * (*m_oddsMult);
//			if (amount > limit)
//				amount = limit;
//			m_amount = amount;
//		}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			int32 limit = 0;
			// bwardlow 5-May-2006 #10761
			// When "laying the odds" on a Don't Pass bet the maximum you can win
			// is the table odds times the amount you bet, but the payout odds that
			// are applied to the Don't Pass Odds bet depend on the point, so
			// calculate the maximum win amount and scale it by the odds.
			int32 maxWin = (int32)(*m_oddsMult * m_depends->getAmount());

			if (m_depends && m_oddsMult)
			{
				switch (point)
				{

					case 0:
						limit = 0;
						break;

					case 4:
					case 10:
						// Don't Pass Odds pay 1-2
						limit = maxWin * 2;
						break;

					case 5:
					case 9:
						// Don't Pass Odds pay 2-3
						limit = (maxWin * 3) / 2;
						break;

					case 6:
					case 8:
						// Don't Pass Odds pay 5-6
						limit = (maxWin * 6) / 5;
						break;

					default:
						// should never happen
						break;
				}

				if (limit > maxLimit)
					limit = maxLimit;

			}
			return limit;
		}


#if 0
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			switch (point)
			{
				case 0:
					return 0;

				// Full double (2.5 x):
				// for bets >= $8 round down to the nearest multiple of $6 (~ 600 cents)
				//     bets <  $8 give 2x odds
				case 6:
				case 8:
					if (m_depends)
					{
						int32 limit	= 0;

						if (*m_oddsMult == 25)
						{
							if (m_depends->getAmount() < 800)
								limit = (int32) (2 * m_depends->getAmount() );
							else
								limit = (int32)( ((int32)(*m_oddsMult * m_depends->getAmount() / 10) / 600) * 600 );
						}
						else
						{
							limit = (int32) (*m_oddsMult / 10 * m_depends->getAmount() );
						}

						if (limit > maxLimit)
							return maxLimit;
						else
							return limit;

					}
					else
						return 0;

				default:
					if (m_depends)
					{
						int32 limit	= 0;
						uint8 mult	= (uint8)(*m_oddsMult);

						if (mult == 25)
							mult = 2;
						else
							mult = mult / 10;

						limit = (int32)(mult * m_depends->getAmount() );

						if (limit > maxLimit)
							return maxLimit;
						else
							return limit;
					}
					else
						return 0;
			}
		}
#endif
};

class FieldBet : public CrapsBet
{
	public:
		FieldBet(uint8 betID) : CrapsBet(betID) { }
		FieldBet(uint8 betID, const FieldBet &bet);
};

class BigBet : public CrapsBet
{
	public:

		BigBet(uint8 betID) : CrapsBet(betID) { }
		BigBet(uint8 betID, const BigBet &bet);

		// can't bet come on the come out roll
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			if (point == 0)	// can't bet on this until after point is made
				return 0;
			else
				return maxLimit;
		}
};


class ComeBet : public CrapsBet
{
	public:
		ComeBet(uint8 betID) : CrapsBet(betID) { }
		ComeBet(uint8 betID, const ComeBet &bet);


		// can't bet come on the come out roll
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			if (point == 0)	// can't bet on this until after point is made
				return 0;
			else
				return maxLimit;
		}

};

class DontComeBet : public ComeBet
{
	public:
		DontComeBet(uint8 betID) : ComeBet(betID) { }
		DontComeBet(uint8 betID, const DontComeBet &bet) :
			ComeBet(betID, bet) {}
};


class ComeSpecificBet : public CrapsBet
{
	public:
		ComeSpecificBet(uint8 betID) : CrapsBet(betID) { }
		ComeSpecificBet(uint8 betID, const ComeSpecificBet &bet);

		bool8 canRemove(uint8 point) { return false; }

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			return 0;	// can't add to this bet
		}
};

class DontComeSpecificBet : public ComeSpecificBet
{
	public:
		DontComeSpecificBet(uint8 betID) : ComeSpecificBet(betID) { }
		DontComeSpecificBet(uint8 betID, const DontComeSpecificBet &bet) :
			ComeSpecificBet(betID, bet) {}
};


class ComeOddsBet : public OddsBet
{
	public:
		ComeOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			OddsBet(betID, depends, oddsMult) {}

		ComeOddsBet(uint8 betID, const ComeOddsBet &bet) :
			OddsBet(betID, bet) {}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			int32 limit = 0;

			if (m_depends && m_oddsMult)
			{
				switch (point)
				{
					case 0:
					default:
						limit = (int32) (*m_oddsMult * m_depends->getAmount() );
						if (limit > maxLimit)
							limit = maxLimit;
						break;
				}
			}
			return limit;
		}

#if 0
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			switch (point)
			{
// Added by GRH
//				case 0:
//					return 0;
// end added by GRH
				default:
					if (m_depends)
					{
						int32 limit	= 0;

						// m_oddsMult of 25 is for full double, but calculate
						// with odds of 2... the 25 is used as a tag
						uint8 mult = (uint8)(*m_oddsMult);

						if (mult == 25)
							mult = 2;
						else
							mult = mult / 10;

						limit = (int32)(mult * m_depends->getAmount() );

						if (limit > maxLimit)
							return maxLimit;
						else
							return limit;
					}
					else
						return 0;
			}
		}
#endif
};

// bwardlow 5-May-2006 #10761
class DontComeOddsBet : public OddsBet
{
	public:
		DontComeOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			OddsBet(betID, depends, oddsMult) {}

		DontComeOddsBet(uint8 betID, const DontComeOddsBet &bet) :
			OddsBet(betID, bet) {}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
			int32 limit = 0;
			// bwardlow 5-May-2006 #10761
			// When "laying the odds" on a Don't Come bet the maximum you can win
			// is the table odds times the amount you bet, but the payout odds that
			// are applied to the Don't Come Odds bet depend on the point, so
			// calculate the maximum win amount and scale it by the odds.
			int32 maxWin = (int32)(*m_oddsMult * m_depends->getAmount());

			if (m_depends && m_oddsMult)
			{
				// the point that is passed in is for the Pass/Don't Pass bets
				// (i.e. the point from the come out roll), what we need to
				// determine is the point that applies to this bet
				switch (m_betID)
				{
					case DontCome4Odds:
					case DontCome10Odds:
						// Don't Come Odds pay 1-2
						limit = maxWin * 2;
						break;

					case DontCome5Odds:
					case DontCome9Odds:
						// Don't Come Odds pay 2-3
						limit = (maxWin * 3) / 2;
						break;

					case DontCome6Odds:
					case DontCome8Odds:
						// Don't Come Odds pay 5-6
						limit = (maxWin * 6) / 5;
						break;

					default:
						// should never happen
						break;
				}

				if (limit > maxLimit)
					limit = maxLimit;

			}
			return limit;
		}
};


// for 6 and 8
class ComeFullDoubleOddsBet : public ComeOddsBet
{
	public:

		ComeFullDoubleOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
		  ComeOddsBet(betID, depends, oddsMult) {}

		ComeFullDoubleOddsBet(uint8 betID, const ComeFullDoubleOddsBet &bet) :
		  ComeOddsBet(betID, bet) {}

		int32 getLimit(uint8 point, int32 maxLimit)
		{
            if (m_depends)
            {
                int32 limit	= 0;

                limit = (int32) (*m_oddsMult * m_depends->getAmount() );
#if 0
                if (*m_oddsMult == 25)
                {
                    if (m_depends->getAmount() < 400)
                        limit = (int32) (2 * m_depends->getAmount() );
                    else if ( (m_depends->getAmount() / 100) % 2 != 0)
                        limit = (int32)( ((*m_oddsMult * m_depends->getAmount() / 10) / 500) * 500 );
                    else
                        limit = (int32) (*m_oddsMult * m_depends->getAmount() / 10);
                }
                else
                {
                    limit = (int32) (*m_oddsMult / 10 * m_depends->getAmount() );
                }
#endif
                if (limit > maxLimit)
                    return maxLimit;
                else
                    return limit;
            }
            else
                return 0;

		}
};


class DontComeFullDoubleOddsBet : public ComeFullDoubleOddsBet
{
	public:
		DontComeFullDoubleOddsBet(uint8 betID, CrapsBet *depends, int32 *oddsMult) :
			ComeFullDoubleOddsBet(betID, depends, oddsMult) {}

		DontComeFullDoubleOddsBet(uint8 betID, const DontComeFullDoubleOddsBet &bet) :
			ComeFullDoubleOddsBet(betID, bet) {}

		// bwardlow 5-May-2006 #10761
		// When "laying the odds" on a Don't Come bet the maximum you can win
		// is the table odds times the amount you bet, but the payout odds that
		// are applied to the Don't Come Odds bet depend on the point, so
		// calculate the maximum win amount and scale it by the odds.
		int32 getLimit(uint8 point, int32 maxLimit)
		{
			int32 limit = 0;
			int32 maxWin = (int32)(*m_oddsMult * m_depends->getAmount());

			if (m_depends && m_oddsMult)
			{
				// the point that is passed in is for the Pass/Don't Pass bets
				// (i.e. the point from the come out roll), what we need to
				// determine is the point that applies to this bet
				switch (m_betID)
				{
					case DontCome4Odds:
					case DontCome10Odds:
						// Don't Come Odds pay 1-2
						limit = maxWin * 2;
						break;

					case DontCome5Odds:
					case DontCome9Odds:
						// Don't Come Odds pay 2-3
						limit = (maxWin * 3) / 2;
						break;

					case DontCome6Odds:
					case DontCome8Odds:
						// Don't Come Odds pay 5-6
						limit = (maxWin * 6) / 5;
						break;

					default:
						// should never happen
						break;
				}

				if (limit > maxLimit)
					limit = maxLimit;

			}
			return limit;
		}
};


class BuyBet : public CrapsBet
{
	public:
		BuyBet(uint8 betID) : CrapsBet(betID) { }
		BuyBet(uint8 betID, const BuyBet &bet);

		int32 getLimit(uint8 point, int32 maxLimit)
		{
// added by GRH
//			if (point == 0)	// can't bet on this until after point is made
//				return 0;
//			else
// added by GRH
				return maxLimit;
		}

};

class PlaceBet : public CrapsBet
{
	public:
		PlaceBet(uint8 betID) : CrapsBet(betID) { }
		PlaceBet(uint8 betID, const PlaceBet &bet);

		int32 getLimit(uint8 point, int32 maxLimit)
		{
// Added by GRH
//			if (point == 0)	// can't bet on this until after point is made
//				return 0;
//			else
// end added by GRH
				return maxLimit;
		}

};


class PlaceLoseBet : public CrapsBet
{
	public:
		PlaceLoseBet(uint8 betID) : CrapsBet(betID) { }
		PlaceLoseBet(uint8 betID, const PlaceLoseBet &bet);

		int32 getLimit(uint8 point, int32 maxLimit)
		{
// Added by GRH
//			if (point == 0)	// can't bet on this until after point is made
//				return 0;
//			else
// End Added by GRH
				return maxLimit;
		}

};


class LayBet : public CrapsBet
{
	public:
		LayBet(uint8 betID) : CrapsBet(betID) { }
		LayBet(uint8 betID, const LayBet &bet);

		int32 getLimit(uint8 point, int32 maxLimit)
		{
// added by GRH
//			if (point == 0)	// can't bet on this until after point is made
//				return 0;
//			else
// end addee by GRH
				return maxLimit;
		}

};

class HardBet : public CrapsBet
{
	public:
		HardBet(uint8 betID) : CrapsBet(betID) { }
		HardBet(uint8 betID, const HardBet &bet);

		// can't bet come on the come out roll
		int32 getLimit(uint8 point, int32 maxLimit)
		{
// Adde by GRH
//			if (point == 0)	// can't bet on this until after point is made
//				return 0;
//			else
// end added by GRH
				return maxLimit;
		}
};

class OneTimeBet : public CrapsBet
{
	public:
		OneTimeBet(uint8 betID) : CrapsBet(betID) { }
		OneTimeBet(uint8 betID, const OneTimeBet &bet);
};

//
// need to write copy constructor and operator= for every new class
// that has extra members
//


inline int32
CrapsBet::getAmount(void)
{
	return m_amount;
}

inline int32
CrapsBet::getBonusAmount(void)
{
	return m_bonusAmount;
}

inline void
CrapsBet::setAmount(int32 amount)
{
	m_amount = amount;
}

inline void
CrapsBet::setBonusAmount(int32 bonusAmount)
{
	m_bonusAmount = bonusAmount;
}

inline void
CrapsBet::setID(uint8 betID)
{
	m_betID = betID;
}

inline void
CrapsBet::setDependant(CrapsBet *bet)
{
	m_dependant = bet;
}

inline void
CrapsBet::grandfathered(bool8 onoff)
{
	m_grandfathered = onoff;
}



#endif
